#pragma once

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning(disable : 4996)
#endif

#include<iostream>
#include"index.hpp"
#include<json/json.h>
#include<algorithm>
#include<vector>
#include<unordered_map>

namespace ns_searcher
{
    class Searcher
    {
    private:
    struct InvertedElemPrint
    {
        InvertedElemPrint():doc_id(0),weight(0)
        {}
        ~InvertedElemPrint()
        {}
        uint64_t doc_id;
        int weight;
        std::vector<std::string> words;
    };
    public:
        Searcher()
        {}
        ~Searcher()
        {}
        //初始化查找系统
        void InitSearcher(const std::string src_path)//我们解析后的html的文件路径
        {
            //1.获取或创建index对象
            index = ns_index::Index::GetInstance();
            LOG(INFO,"获取index单例成功...");  
            //std::cout << "获取index单例成功" << std::endl;
            //2.根据index对象建立索引
            index->BuildIndex(src_path);
            LOG(INFO,"构建索引成功...");  
            //std::cout << "构建索引成功" << std::endl;
        }
        //调用查找
        //query: 查询关键字
        //json_str: 返回给用户浏览器的搜索结果(要用jsoncpp来序列化)
        void Search(const std::string& query,std::string* json_str)//输入查询语句(关键词)和输出json串
        {
            //1.[分词]对我们传入的查询词语利用jieba分词，找到我们需要的关键字
            std::vector<std::string> words;
            ns_util::JieBaUtil::CutString(query,&words);
            //2.[触发]根据分词的各个关键词进行index查找（index不分大小写，这里也不分）
            //用来存放我们所有倒排拉链中的倒排元素
            //ns_index::inverted_list inverted_list_all;--不去重版本
            std::vector<InvertedElemPrint> inverted_list_all;
            //存储我们的根据id去重后的倒排元素信息
            std::unordered_map<uint64_t,InvertedElemPrint> token_map;
            for(auto word : words)
            {
                //利用boost库转换小写
                boost::to_lower(word);
                //通过倒排索引获得我们的倒排拉链
                ns_index::inverted_list* inverted_list = index->GetInvertedList(word);
                if(inverted_list == nullptr)
                {
                    continue;
                }

                for(auto& elem : *inverted_list)
                {
                    auto& item = token_map[elem.id];
                    item.doc_id = elem.id;
                    item.weight += elem.weight;
                    item.words.push_back(elem.word);
                }
                //这里获取倒排拉链会存在问题，比如说雷军买小米,分成雷军、小米、买小米，而他们可能都同时存在于同一文档中,因此获取的倒排拉链也就有三个
                //每个拉链中都包含同一文档，这是不正确的，我们只需要一份文档内容即可，但是不同关键字的相关性可以加在一起，因此我们内容去重，但是相关性可以加在一起
                //inverted_list_all.insert(inverted_list_all.end(),inverted_list->begin(),inverted_list->end());
            }

            //将去重的结果保存在我们的拉链中
            for(auto &item : token_map)
            {
                inverted_list_all.push_back(std::move(item.second));
            }

            //3.[合并排序]汇总查找结果，以相关性进行降序排序
            // std::sort(inverted_list_all.begin(),inverted_list_all.end(),\
            // [](const ns_index::InvertedElem& e1,const ns_index::InvertedElem& e2){
            //     return e1.weight > e2.weight;
            // });
            std::sort(inverted_list_all.begin(),inverted_list_all.end(),\
            [](const InvertedElemPrint& e1,const InvertedElemPrint& e2){
                return e1.weight > e2.weight;
            });

            //4.[构建]根据结果，构建json串----jsoncpp外部库来构建
            Json::Value root;
            for(auto& elem : inverted_list_all)
            {
                ns_index::DocInfo* doc = index->GetForwardIndex(elem.doc_id);
                Json::Value item;
                item["title"] = doc->title;
                item["desc"] = GetDest(doc->content,elem.words[0]);//我们实际的正文内容并不需要所有，只要一些描述即可，因此还需要修改
                item["url"] = doc->url;

                //for dubeg
                //item["id"] = (int)elem.doc_id;
                //item["weight"] = elem.weight;
                root.append(item);
            }
            //fordebug
            //Json::StyledWriter writer;
            Json::FastWriter writer;
            //输出我们的json字符串
            *json_str = writer.write(root);
        }

    private:

        std::string GetDest(const std::string& content,const std::string&word)
        {
            //找到word再我们content中首次出现的位置pos
            //然后从pos往前找50字节作为我们描述的起始start，往后找100字节作为我们的结束end
            //但是往前往后可能失败，如果不足的话，我们就按照原始大小开始
            //最后从start到end截取这部分内容
            const size_t prev_step = 50;
            const size_t next_step = 100;


            //由于content的内容分大小写，而我们倒排拉链中的关键词都不分，全为小写，因此不能用find查找
            //1.查找关键字位置
            // size_t pos = content.find(word);
            // if(pos == std::string::npos)
            // {
            //     return "None1";//这种情况不存在，因为我们就是从内容获取的word，
            // }
            //我们改用c++/algorithm的search方法
            auto iter = std::search(content.begin(),content.end(),word.begin(),word.end(),
            [](int x,int y){return (std::tolower(x) == std::tolower(y));
            });
            if(iter == content.end())
            {
                return "None1";
            }
            size_t pos = std::distance(content.begin(),iter);


            size_t start = 0;//默认起始
            size_t end = content.size()-1;//默认结束

            //2.更新起始位置
            if(pos > prev_step + start) start = pos-prev_step;
            if(pos + next_step < end) end = pos+next_step;

            //3.截取字符串

            if(start >= end) return "None2";
            std::string desc = content.substr(start,end - start + 1);
            desc += "...";

            return desc;
        }
        ns_index::Index* index;//共服务系统查找的索引
    };
}